package com.blue.core.http;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.DisposableBean;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * Http Async 简化调用
 * 
 * @author zhengj
 * @since 1.0 2017年8月11日
 */
public final class HttpAsyncInvoker implements DisposableBean
{
	//private static Logger logger = LoggerFactory.getLogger(HttpAsyncInvoker.class);

	private final static int TIMEOUT = 60000;
	private final static int MAX_TOTAL = 200;
	private final static int MAX_PER_ROUTE = 200;
	
	private final PoolingNHttpClientConnectionManager connMgr;
	private final CloseableHttpAsyncClient httpAsyncClient;
	
	/**
	 * 创建Http调用类，信任所有https
	 */
	public HttpAsyncInvoker()
	{
		this(true);
	}
	
	/**
	 * 创建Http调用类
	 * @param trust，是否信任所有https
	 */
	public HttpAsyncInvoker(boolean trust)
	{
		IOReactorConfig ioReactorConfig =  IOReactorConfig.custom()
				.setSoTimeout(TIMEOUT)
				.setConnectTimeout(TIMEOUT)
				.setTcpNoDelay(true)
				.build();
		RegistryBuilder<SchemeIOSessionStrategy> registryBuilder = RegistryBuilder.<SchemeIOSessionStrategy>create();
		registryBuilder.register("http", NoopIOSessionStrategy.INSTANCE);
		ConnectingIOReactor ioReactor = null;
		try
		{
			if (trust)
			{
				//SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE).build();
				SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, type) -> true).build();
				registryBuilder.register("https", new SSLIOSessionStrategy(sslContext, new DefaultHostnameVerifier()));
			}
			else
			{
				registryBuilder.register("https", NoopIOSessionStrategy.INSTANCE);
			}
			ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		connMgr = new PoolingNHttpClientConnectionManager(ioReactor, registryBuilder.build());
		connMgr.setMaxTotal(MAX_TOTAL);
		connMgr.setDefaultMaxPerRoute(MAX_PER_ROUTE);
		
		RequestConfig requestConfig = RequestConfig.custom()
				.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
				.setConnectionRequestTimeout(TIMEOUT)
				.setConnectTimeout(TIMEOUT)
				.build();
		
		httpAsyncClient = HttpAsyncClients.custom()
				.setConnectionManager(connMgr)
				.setDefaultRequestConfig(requestConfig)
				.build();
		httpAsyncClient.start();
	}
	
	/**
	 * 异步 GET 请求
	 * 
	 * @param url 网址
	 * @param callback 结果回调
	 * @param headers HTTP头数组
	 * @return 
	 */
	public Future<HttpResponse> get(String url, FutureCallback<HttpResponse> callback, Header...headers)
	{
		HttpGet request = new HttpGet(url);
		if (headers.length > 0)
		{
			request.setHeaders(headers);
		}
		return httpAsyncClient.execute(request, callback);
	}
	
	/**
	 * 异步 POST 请求
	 * 
	 * @param url 网址
	 * @param param POST提交内容
	 * @param callback 结果回调
	 * @param headers HTTP头数组
	 * @return
	 */
	public Future<HttpResponse> post(String url, String param, FutureCallback<HttpResponse> callback, Header...headers)
	{
		StringEntity entity = new StringEntity(param, Consts.UTF_8);
		HttpPost request = new HttpPost(url);
		if (headers.length > 0)
		{
			request.setHeaders(headers);
		}
		request.setEntity(entity);
		return httpAsyncClient.execute(request, callback);
	}
	
	/**
	 * 异步 POST 请求
	 * 
	 * @param url 网址
	 * @param param POST提交内容
	 * @param callback 结果回调
	 * @param headers HTTP头数组
	 * @return
	 */
	public Future<HttpResponse> post(String url, Map<String, Object> param, FutureCallback<HttpResponse> callback, Header...headers)
	{
		List<NameValuePair> pairList = new ArrayList<>();
		for (Map.Entry<String, Object> entry : param.entrySet())
		{
			pairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
		}
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairList, Consts.UTF_8);
		HttpPost request = new HttpPost(url);
		if (headers.length > 0)
		{
			request.setHeaders(headers);
		}
		request.setEntity(entity);
		return httpAsyncClient.execute(request, callback);
	}

	@Override
	public void destroy()
	{
		try
		{
			httpAsyncClient.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	
}
